#include <iostream>
#include <string>
#include <stdlib.h>
#include <getopt.h>

#include "util.cpp"
#include "Parser.h"
#include "calculo_tamanio_bloque.cpp"
#include "calculo_tamanio_cache.cpp"
#include "calculo_cantidad_vias.cpp"

using namespace std;

void imprimir_help(char* argv0) {
  std::cout << "usage: " << argv0 << " --D1=<size>,<associativity>,<line size>" << std::endl;
  std::cout << "       " << argv0 << " [other options]" << std::endl;
  std::cout << std::endl;
  std::cout << "Options:" << std::endl;
  std::cout << "  --D1=<size>,<associativity>,<line size>   Specify the size, associativity and line size of the level 1 data cache." << std::endl;
  std::cout << "  -h, --help     		            Prints this help and quit." << std::endl;
  std::cout << "  -v, --version  			    Prints version information and quit." << std::endl;
}

bool verificar_d1(char* d1) {
  unsigned int i, cant= 0;
  char c;
  
  for(i= 0; (c= d1[i]) != ','; i++) {
    cant++;
    if(c != '\0') {
      if(!isdigit(c))
	return false;
    } else
      return false;
  }
  if(cant == 0)
    return false;

  c= d1[++i];
  cant= 0;
  for(; (c= d1[i]) != ','; i++) {
    cant++;
    if(c != '\0') {
      if(!isdigit(c))
	return false;
    } else
      return false;
  }
  if(cant == 0)
    return false;

  c= d1[++i];
  cant= 0;
  for(; (c= d1[i]) != '\0'; i++) {
    cant++;
    if(!isdigit(c))
      return false;
  }
  if(cant == 0)
    return false;
  
  return true;
}

string parsearOpciones(int argc, char* argv[]) {
  const char* const short_options = "hv";
  const struct option long_options[] = {{"help", 0, NULL, 'h' }, {"version", 0, NULL, 'v' }, { "D1", 1,  NULL, 'd' }, {NULL, 0, NULL, 0}};

  int c;
  string d1("");
  while ((c = getopt_long(argc, argv, short_options, long_options, NULL)) != -1) {
    switch (c) {
      case 'h': //help
        imprimir_help(argv[0]);
        exit(0);
        break;
      case 'v': //version
        std::cout << "66.20 Organización de computadoras - Trabajo práctico 2" << std::endl;
        std::cout << "version 1.0" << std::endl;
        exit(0);
        break;
      case 'd' : //D1
        if (verificar_d1(optarg) == false) {
          imprimir_help(argv[0]);
          exit(1);
        }
        d1.append(optarg);
        break;
      case '?': //opción no valida
        imprimir_help(argv[0]);
        exit(1);
      case -1: // No hay más opciones
        break;
      default:
        exit(1); //inesperado	
    }
  }
  return d1;
}

int main(int argc, char* argv[]) {

  //Verifico parametros ingresados
  string d1 = parsearOpciones(argc, argv);
  if(d1 == "") {
    std::cerr << "Debe ingresar el tamanio de la memoria de datos D1" << std::endl;
    imprimir_help(argv[0]);
    exit(1);
  }

  //Empiezo a calcular tamanio bloque y cache + vias
  int tamanioBloque= 0, tamanioCache= 0, vias= 0;
  Parser* parser= new Parser();

  tamanioBloque= calcularTamanioBloque(parser, d1);
  tamanioCache= calcularTamanioCache(parser, tamanioBloque, d1);
  vias= calcularCantidadVias(parser, tamanioCache, tamanioBloque, d1);

 //Elimino todos los archivos salida del valgrind
 remove(SALIDA_VALGRIND);
 remove(SALIDA_CACHEGRIND);
 remove(SALIDA_CGANNOTATE);
 
  if(tamanioBloque == -1 || tamanioCache == -1 || vias == -1) {
    std::cerr << "Error al ejecutar el valgrind." << std::endl;
    exit(1);
  }
 
  //Muestro resultados
  std::cout << "Memoria Cache de Datos Analizada:" << std::endl;
  std::cout << "#Vias: " << vias << " Vias" << std::endl;
  std::cout << "Tamaño: " << tamanioCache << " Bytes" << std::endl;
  std::cout << "Tamaño de bloque: " << tamanioBloque << " Bytes" << std::endl;

  return 0;
}
